<!DOCTYPE html>
<html>
<head>
<meta name="viewport"
	content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
<meta charset="UTF-8">

<!-- tdk start -->
<title>WeakHashMap</title>
<meta name="Keywords" content="WeakHashMap，消除对象的引用，内存泄漏" />
<meta name="Description" content="" />
<!-- tdk end -->

</head>
<link type="text/css" rel="stylesheet" href="../styles/main_text.css" />
<body>
	<div class="content">

		<!-- 导航栏 start-->
		<a href="../index.html">目录</a> > <a href="../effective_java.html">高效Java</a>

		<!-- 导航栏 end -->


		<h1>WeakHashMap</h1>
		<span>请尊重版权！ 如需转载，请联系<a href="#wechat">作者<a />。</span>
		<hr style="margin-bottom: 50px;" />

		<!-- 正文内容 start-->
		<div class="main_text">
			<h2>当value为空</h2>
			<p>放入Map中的对象，Map会保持对其的引用。而保证其不在GC回收的范围。这是Map的一个特点。我们看一段代码</p>
			<div class="code_area">
				<span><span style="color: red">[程序清单03] </span></span>
				<pre class="brush: java">
public static void main(String[] args) {

	Map&lt;Object, Object&gt; map = new HashMap&lt;&gt;();

	Foo foo = new Foo("foo");
	map.put("foo", foo);
	foo = null;
	
	System.gc();
	Thread.sleep(2000);
	System.out.println("size : " + map.size());
	System.out.println("foo : " + map.get(foo));
	for (Entry&lt;Object, Object&gt; en : map.entrySet()) {
		Object key = en.getKey();
		Object value = en.getValue();
		System.out.println("key:" + key + "\nvalue:" + value);
	}

}
</pre>
				<p>输出为</p>
				<blockquote>
					size : 1<br /> foo : null<br /> key:Foo [name=foo]<br />
					value:foo<br />
				</blockquote>
			</div>
			<p>
				在上例中，<b>foo被置为空后失去了引用的对象，但map依然保持着这个引用<i>（new
						Foo("foo")的内存地址值），</i>， 以至于这个对象没有被GC回收。
				</b>
			</p>
			<h2>当key为空</h2>
			<p>当key为空的时候，HashMap和本文要讲的WeakHashMap，完全相反。</p>
			<div class="code_area">
				<span><span style="color: red">[程序清单04] </span>HashMap中的对象引用被置null</span>
				<pre class="brush: java">
Map&lt;Object, Object&gt; map = new HashMap&lt;&gt;();

Foo foo = new Foo("foo");
map.put("foo", foo);
foo = null;

System.gc();
Thread.sleep(2000);
System.out.println("size : " + map.size());
System.out.println("foo : " + map.get(foo));
for (Entry&lt;Object, Object&gt; en : map.entrySet()) {
	Object key = en.getKey();
	Object value = en.getValue();
	System.out.println("key:" + key + "\nvalue:" + value);
}
</pre>
				<p>输出为</p>
				<blockquote>
					size : 1<br /> foo : null<br /> key:Foo [name=foo]<br />
					value:foo<br />
				</blockquote>
			</div>
			<p>
				在上例中，同样，foo被置为空后失去了引用的对象，但map的key中依然保持着这个引用<i>（new
					Foo("foo")的内存地址值）</i>，
				以至于这个对象没有被GC回收。当然我们不可以再使用foo去作为key去get，因为foo指向的地址已经被清空了。而它依然存在于map中。
			</p>
			<br />
			<p>下面是一段WeakHashMap的例子</p>
			<span><span style="color: red">[程序清单05] </span>WeakHashMap的key被置null</span>
			<pre class="brush: java">
Map&lt;Object, Object&gt; map = new WeakHashMap&lt;&gt;();

Foo foo = new Foo("foo");
map.put("foo", foo);
foo = null;

System.gc();
Thread.sleep(2000);
System.out.println("size : " + map.size());
System.out.println("foo : " + map.get(foo));
for (Entry&lt;Object, Object&gt; en : map.entrySet()) {
	Object key = en.getKey();
	Object value = en.getValue();
	System.out.println("key:" + key + "\nvalue:" + value);
}
</pre>
			<p>输出为</p>
			<blockquote>
				size : 0<br /> foo : null
			</blockquote>
			<p>
				上例中，map的实现类使用了WeakHashMap，<b>map中被置null的key，在gc后被清除了。在适当的场景中使用，可以有效避免内存泄漏。</b>
			</p>

			<h2>API中对WeakHashMap的描述</h2>
			<p>
				引述自：<a
					href="http://docs.oracle.com/javase/7/docs/api/java/util/WeakHashMap.html">Java
					API WeakHashMap</a>
			</p>
			<blockquote>
				<p>Hash table based implementation of the Map interface, with
					weak keys. An entry in a WeakHashMap will automatically be removed
					when its key is no longer in ordinary use. More precisely, the
					presence of a mapping for a given key will not prevent the key from
					being discarded by the garbage collector, that is, made
					finalizable, finalized, and then reclaimed. When a key has been
					discarded its entry is effectively removed from the map, so this
					class behaves somewhat differently from other Map implementations.</p>
				<p>Both null values and the null key are supported. This class
					has performance characteristics similar to those of the HashMap
					class, and has the same efficiency parameters of initial capacity
					and load factor.</p>

				<p>Like most collection classes, this class is not synchronized.
					A synchronized WeakHashMap may be constructed using the
					Collections.synchronizedMap method.</p>

				<p>This class is intended primarily for use with key objects
					whose equals methods test for object identity using the ==
					operator. Once such a key is discarded it can never be recreated,
					so it is impossible to do a lookup of that key in a WeakHashMap at
					some later time and be surprised that its entry has been removed.
					This class will work perfectly well with key objects whose equals
					methods are not based upon object identity, such as String
					instances. With such recreatable key objects, however, the
					automatic removal of WeakHashMap entries whose keys have been
					discarded may prove to be confusing.</p>

				<p>The behavior of the WeakHashMap class depends in part upon
					the actions of the garbage collector, so several familiar (though
					not required) Map invariants do not hold for this class. Because
					the garbage collector may discard keys at any time, a WeakHashMap
					may behave as though an unknown thread is silently removing
					entries. In particular, even if you synchronize on a WeakHashMap
					instance and invoke none of its mutator methods, it is possible for
					the size method to return smaller values over time, for the isEmpty
					method to return false and then true, for the containsKey method to
					return true and later false for a given key, for the get method to
					return a value for a given key but later return null, for the put
					method to return null and the remove method to return false for a
					key that previously appeared to be in the map, and for successive
					examinations of the key set, the value collection, and the entry
					set to yield successively smaller numbers of elements.</p>

				<p>Each key object in a WeakHashMap is stored indirectly as the
					referent of a weak reference. Therefore a key will automatically be
					removed only after the weak references to it, both inside and
					outside of the map, have been cleared by the garbage collector.</p>

				<p>Implementation note: The value objects in a WeakHashMap are
					held by ordinary strong references. Thus care should be taken to
					ensure that value objects do not strongly refer to their own keys,
					either directly or indirectly, since that will prevent the keys
					from being discarded. Note that a value object may refer indirectly
					to its key via the WeakHashMap itself; that is, a value object may
					strongly refer to some other key object whose associated value
					object, in turn, strongly refers to the key of the first value
					object. If the values in the map do not rely on the map holding
					strong references to them, one way to deal with this is to wrap
					values themselves within WeakReferences before inserting, as in:
					m.put(key, new WeakReference(value)), and then unwrapping upon each
					get.</p>

				<p>The iterators returned by the iterator method of the
					collections returned by all of this class's "collection view
					methods" are fail-fast: if the map is structurally modified at any
					time after the iterator is created, in any way except through the
					iterator's own remove method, the iterator will throw a
					ConcurrentModificationException. Thus, in the face of concurrent
					modification, the iterator fails quickly and cleanly, rather than
					risking arbitrary, non-deterministic behavior at an undetermined
					time in the future.</p>

				<p>Note that the fail-fast behavior of an iterator cannot be
					guaranteed as it is, generally speaking, impossible to make any
					hard guarantees in the presence of unsynchronized concurrent
					modification. Fail-fast iterators throw
					ConcurrentModificationException on a best-effort basis. Therefore,
					it would be wrong to write a program that depended on this
					exception for its correctness: the fail-fast behavior of iterators
					should be used only to detect bugs.</p>

				<p>This class is a member of the Java Collections Framework.
			</blockquote>

			<p>可以总结出以下几点：</p>
			<ol>
				<li>WeakHashMap中的条目在其键不再被使用时将自动被删除</li>
				<li>支持null键和null值</li>
				<li>该类具有与HashMap类相似的性能特征</li>
				<li>不同步。可以使用Collections.synchronizedMap方法构建一个同步的WeakHashMap</li>
				<li>判断key对象是否相等使用的是equals方法，默认的是使用<code>==</code>操作符<i>（你应该知道，使用<code>==</code>操作符即是比较内存地址值）
				</i></li>
				<li>当一个key指向一个<b>弱引用</b><sub><a href="">点击查看弱引用 </a></sub>时，会被自动移除，并被垃圾回收器回收。
				</li>
				<li>WeakHashMap中的值对象由普通的强引用保存。因此，应注意确保值对象不直接或间接强烈地引用其自己的键，因为这将阻止键被丢弃。
					注意，value对象可以通过WeakHashMap本身间接地引用其键; 也就是说，value对象可能强烈地引用一些其他关键对象，
					其关联值对象又强烈地引用第一个值对象的键。
					如果map中的值不依赖于强调引用它们的map，则处理此方法的一种方法是在插入之前将值自身包含在WeakReferences中，
					如：m.put（key，new WeakReference（value）），然后在每个get上打包</li>
				<li>关于迭代器：如果不使用迭代器自己的remove方法，会抛出一个<code>ConcurrentModificationException</code></li>
			</ol>
			<h2>WeakHashMap的实现原理</h2>
			<p>WeakHashMap的类继承结构</p>
			<br />
			<p>
				<img src="../imgs/11.jpg"><br />引自网络
			</p>
			<br />
			<p>在JDK源码中可以看到WeakHashMap有这么一段代码</p>
			<pre class="brush: java">
private final ReferenceQueue&lt;Object&gt; queue = new ReferenceQueue&lt;&gt;();
/**
 * Expunges stale entries from the table.
 */
private void expungeStaleEntries() {
	for (Object x; (x = queue.poll()) != null; ) {
		synchronized (queue) {
			@SuppressWarnings("unchecked")
				Entry&lt;K,V&gt; e = (Entry&lt;K,V&gt;) x;
			int i = indexFor(e.hash, table.length);

			Entry&lt;K,V&gt; prev = table[i];
			Entry&lt;K,V&gt; p = prev;
			while (p != null) {
				Entry&lt;K,V&gt; next = p.next;
				if (p == e) {
					if (prev == e)
						table[i] = next;
					else
						prev.next = next;
					// Must not null out e.next;
					// stale entries may be in use by a HashIterator
					e.value = null; // Help GC
					size--;
					break;
				}
				prev = p;
				p = next;
			}
		}
	}
}
</pre>
			<p>据此，可以总结出其执行步骤如下：</p>
			<ol>
				<li>遍历queue中的Entry元素</li>
				<li>获取当前元素索引i</li>
				<li>通过i，获取table中的元素pre</li>
				<li>如果pre存在，则将pre后面的元素往前挪动一个pos。自此，pre的引用丢失，将被GC回收。pre对应的value被置null，也将被GC回收。</li>
			</ol>
			<p>
				<code>expungeStaleEntries</code>
				方法会在get和put方法执行的时候被调用，当然是是通过
				<code>getTable</code>
				方法调用的
			</p>
			<pre class="brush: java">
public V get(Object key) {
	Object k = maskNull(key);
	int h = hash(k);
	Entry&lt;K,V&gt;[] tab = getTable();
	int index = indexFor(h, tab.length);
	Entry&lt;K,V&gt; e = tab[index];
	while (e != null) {
		if (e.hash == h && eq(k, e.get()))
			return e.value;
		e = e.next;
	}
	return null;
}

public V put(K key, V value) {
	Object k = maskNull(key);
	int h = hash(k);
	Entry&lt;K,V&gt;[] tab = getTable();
	int i = indexFor(h, tab.length);

	for (Entry&lt;K,V&gt; e = tab[i]; e != null; e = e.next) {
		if (h == e.hash && eq(k, e.get())) {
			V oldValue = e.value;
			if (value != oldValue)
				e.value = value;
			return oldValue;
		}
	}

	modCount++;
	Entry&lt;K,V&gt; e = tab[i];
	tab[i] = new Entry&lt;&gt;(k, value, queue, h, e);
	if (++size &gt;= threshold)
		resize(tab.length * 2);
	return null;
}	

private Entry&lt;K,V&gt;[] getTable() {
	expungeStaleEntries();
	return table;
}
</pre>
			<br />
			<p>如果你看过WeakHashMap的源码，可能会疑惑，这里只有queue的poll方法被调用，却没有见到queue入队列方法呢？</p>
			<p>
				此处做简要说明<sub><a href="">详细可参见Java中的引用类型</a></sub>，要点是以下几点
			<ol>
				<li>map中的entry实现了WeakReference类</li>
				<li>在实例化entry的时候传入了queue<code>tab[i] = new Entry<>(k,
						value, queue, h, e);</code></li>
				<li>在WeakReference的父类中，会有一个线程，将符合条件的弱引用对象加入queue中，相关的代码如下：
					<ul>
						<li><code>java.lang.ref.Reference ： if (q !=
								ReferenceQueue.NULL) q.enqueue(r);</code></li>
						<li><code>java.lang.ref.ReferenceQueue ：
								sun.misc.VM.addFinalRefCount(1);</code></li>
				</li>
				</ul>
			</ol>
			</p>
			<h2>WeakHashMap的引用场景</h2>
			<p>WeakHashMap经常被用在缓存中，来避免无意识的对象保持造成的内存泄漏</p>
		</div>

		<!-- 正文内容 end-->

		<!--底部 start  -->
		<hr style="margin-top: 50px;" />
		<p>相关阅读</p>
		<a
			href="http://docs.oracle.com/javase/7/docs/api/java/util/WeakHashMap.html">Java
			API WeakHashMap</a><br /> <a
			href="./release_object_and_memory_leak.html">站内：消除过期对象的引用与内存泄漏</a><br />
		<a href="">站内：Java中的引用类型</a><br />

		<hr style="margin-top: 50px;" />
		<p>
			<span style="color: red">你可以将你知道的WeakHashMap应用场景，通过底部的wechat发送给我，来完善此页面，帮助更多的人学习。</span>
		</p>
		<p><a href="/tks.html">鸣谢</a></p>
		<div style="margin-bottom: 50px;">
			<table>
				<tr>
					<td style="font-weight: 300" height="" width="200px">
						<div style="margin-top: 10px;">
							加好友 <img style="margin-top: 5px;" id="wechat" width="170px;"
								src="../imgs/wechat.png"> <br />扫描分享到朋友圈
							<p id="qrcode"
								style="margin-top: 5px; width: 185px; height: 188px; margin: 0;"></p>
							<script type="text/javascript" src="../scripts/qrcode.min.js">
								
							</script>
							<script type="text/javascript" src="../scripts/jquery.min.js"></script>
							<script type="text/javascript">
								var qrcode = new QRCode(document
										.getElementById("qrcode"), {
									width : 170,
									height : 170
								});

								function makeCode() {
									qrcode.makeCode(window.location.href);
								}
								makeCode();
							</script>

						</div>
					</td>
					<td>
						<div
							style="margin-top: 10px; margin-left: 100px; background: rgba(224, 224, 224, 0.22); border: 1px solid rgba(224, 224, 224, 0.22);">
							<div style="margin: 20px;">
								<p style="font-weight: 300">
									打赏
									<p style="color: blue">如果文中的技术在企业开发中，解决了你的问题，或者避免了你踩坑，请你付费观看，建议30元，但是这不是强制的。针对学生免费。

									
									
								<div>
										<img width="200px;" src="../imgs/we_pay.png"><img
										width="200px;" src="../imgs/ali_pay.png">

									</div>
								</div>
							</div>
						</td>
					</tr>
					<tr>

					</tr>
				</table>
			</div>
		<!--底部 end  --></
								div>
</body>

<script type="text/javascript" src="../scripts/XRegExp.js"></script>
<!-- XRegExp is bundled with the final shCore.js during build -->
<script type="text/javascript" src="../scripts/shCore.js"></script>
<script type="text/javascript" src="../scripts/shBrushJScript.js"></script>
<script type="text/javascript" src="../scripts/shBrushJava.js"></script>
<link type="text/css" rel="stylesheet" href="../styles/shCore.css" />
<link type="text/css" rel="Stylesheet"
	href="../styles/shThemeDefault.css" />
<script type="text/javascript">
	//SyntaxHighlighter.all();
</script>

</html>
<script>
	function IsPC() {
		var userAgentInfo = navigator.userAgent;
		var Agents = [ "Android", "iPhone", "SymbianOS", "Windows Phone",
				"iPod" ];
		var flag = true;
		for (var v = 0; v < Agents.length; v++) {
			if (userAgentInfo.indexOf(Agents[v]) > 0) {
				flag = false;
				break;
			}
		}
		if (window.screen.width >= 768) {
			flag = true;
		}
		return flag;
	}
	if (!IsPC()) {
		$(".content").css("width", "100%");
		$("img").css("width", "100%");
		$("pre").css("width", "100%");
		$("pre").css("overflow", "auto");
		/**
		$("pre").css("white-space", "pre-wrap");
		$("pre").css("word-wrap", "break-word");
		 **/

	} else {
		SyntaxHighlighter.all();
	}
</script>